﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security;

namespace Konves.Csv.Serialization
{
    /// <summary>
    /// Implements a <see cref="System.IO.StreamReader"/> that reads CSV records from a byte stream in a particular encoding.
    /// </summary>
    public class CsvStreamReader : StreamReader
    {
        #region == Constructors ==
        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified stream.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream)
            : this(stream, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified stream.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, char delimiter, char escape)
            : base(stream)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified file name.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path)
            : this(path, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified file name.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, char delimiter, char escape)
            : base(path)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified stream, with the specified byte order mark detection option.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
            : this(stream, detectEncodingFromByteOrderMarks, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified stream, with the specified byte order mark detection option.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, bool detectEncodingFromByteOrderMarks, char delimiter, char escape)
            : base(stream, detectEncodingFromByteOrderMarks)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified stream, with the specified character encoding.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, Encoding encoding)
            : this(stream, encoding, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified stream, with the specified character encoding.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, Encoding encoding, char delimiter, char escape)
            : base(stream, encoding)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified file name, with the specified byte order mark detection option.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, bool detectEncodingFromByteOrderMarks)
            : this(path, detectEncodingFromByteOrderMarks, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified file name, with the specified byte order mark detection option.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, bool detectEncodingFromByteOrderMarks, char delimiter, char escape)
            : base(path, detectEncodingFromByteOrderMarks)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified file name, with the specified character encoding.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, Encoding encoding)
            : this(path, encoding, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified file name, with the specified character encoding.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, Encoding encoding, char delimiter, char escape)
            : base(path, encoding)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// stream, with the specified character encoding and byte order mark detection option.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
            : this(stream, encoding, detectEncodingFromByteOrderMarks, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// stream, with the specified character encoding and byte order mark detection option.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> is null.</exception>
        public CsvStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, char delimiter, char escape)
            : base(stream, encoding, detectEncodingFromByteOrderMarks) 
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// file name, with the specified character encoding and byte order mark detection option.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
            : this(path, encoding, detectEncodingFromByteOrderMarks, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// file name, with the specified character encoding and byte order mark detection option.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, char delimiter, char escape)
            : base(path, encoding, detectEncodingFromByteOrderMarks)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// stream, with the specified character encoding, byte order mark detection option, and buffer size.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="bufferSize">The minimum buffer size.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> or <paramref name="encoding">encoding</paramref> is null.</exception>
        ///     
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        ///     <paramref name="bufferSize">BufferSize</paramref> is less than or equal to zero.</exception>
        public CsvStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
            : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// stream, with the specified character encoding, byte order mark detection option, and buffer size.
        /// </summary>
        /// <param name="stream">The stream to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="bufferSize">The minimum buffer size.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///     <paramref name="stream">Stream</paramref> does not support reading.</exception>
        ///     
        /// <exception cref="T:System.ArgumentNullException">
        ///     <paramref name="stream">Stream</paramref> or <paramref name="encoding">encoding</paramref> is null.</exception>
        ///     
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        ///     <paramref name="bufferSize">BufferSize</paramref> is less than or equal to zero.</exception>
        public CsvStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, char delimiter, char escape)
            : base(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// file name, with the specified character encoding, byte order mark detection option, and buffer size.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="bufferSize">The minimum buffer size, in number of 16-bit characters.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        ///   
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        ///   <paramref name="buffersize">Buffersize</paramref> is less than or equal to zero. </exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
            : this(path, encoding, detectEncodingFromByteOrderMarks, bufferSize, defaultDelimiter, defaultEscape) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Konves.Csv.Serialization.CsvStreamReader"/> class for the specified
        /// file name, with the specified character encoding, byte order mark detection option, and buffer size.
        /// </summary>
        /// <param name="path">The complete file path to be read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
        /// <param name="bufferSize">The minimum buffer size, in number of 16-bit characters.</param>
        /// <param name="delimiter">The character used to delimit fields.</param>
        /// <param name="escape">The character used to escape fields.</param>
        /// <exception cref="T:System.ArgumentException">
        ///   <paramref name="path">Path</paramref> is an empty string ("").</exception>
        ///   
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="path">Path</paramref> or <paramref name="encoding"/> is null.</exception>
        ///   
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found.</exception>
        ///   
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        ///   
        /// <exception cref="T:System.NotSupportedException">
        ///   <paramref name="path">Path</paramref> includes an incorrect or invalid syntax for file name, directory name, or volume label.</exception>
        ///   
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        ///   <paramref name="buffersize">Buffersize</paramref> is less than or equal to zero. </exception>
        [SecuritySafeCritical]
        public CsvStreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, char delimiter, char escape)
            : base(path, encoding, detectEncodingFromByteOrderMarks, bufferSize)
        {
            this.Delimiter = delimiter;
            this.EscapeCharacter = escape;
        }
        #endregion

        #region Delimiter
        /// <summary>
        /// Gets the character used to delimit fields.
        /// </summary>
        public char Delimiter { get; private set; }
        private const char defaultDelimiter = ',';
        #endregion

        #region EscapeCharacter
        /// <summary>
        /// Gets the character used to escape fields.
        /// </summary>
        public char EscapeCharacter { get; private set; }
        private const char defaultEscape = '"';
        #endregion

        #region ReadRecord
        /// <summary>
        /// Reads a CSV record from the current stream and returns the data as a collection of strings.
        /// </summary>
        /// <returns>
        /// The next CSV record from the input stream, or null if the end of the input stream is reached.
        /// </returns>
        /// <exception cref="T:System.OutOfMemoryException">
        ///     There is insufficient memory to allocate a buffer for the returned strings.</exception>
        ///   
        /// <exception cref="T:System.IO.IOException">
        ///     An I/O error occurs.</exception>
        ///     
        /// <exception cref="T:System.FormatException">
        ///     The input stream contains mal-formatted CSV data.</exception>
        public string[] ReadRecord()
        {
            return this._readRecord().ToArray();
        }
        private IEnumerable<string> _readRecord()
        {
            string data = base.ReadLine();

            bool beforeEsc = true;
            bool inEsc = false;
            bool afterEsc = false;
            bool canEsc = true;

            bool isdblesc = false;

            char d = this.Delimiter;
            char e = this.EscapeCharacter;

            char cr = '\r';
            char lf = '\n';

            int datastarts = 0;

            for (int i = 0; i < data.Length; i++)
            {
                #region beforeEsc
                if (beforeEsc)
                {
                    #region whitespace
                    if (Char.IsWhiteSpace(data[i]) && !(data[i] == cr && data.Length > i + 1 && data[i + 1] == lf)) // whitespce (not linebreak)
                    {
                    }
                    #endregion
                    #region escape character
                    else if (data[i] == e) // escape character
                    {
                        if (canEsc)
                        {
                            beforeEsc = false;
                            inEsc = true;
                            afterEsc = false;

                            datastarts = i + 1;
                        }
                        else
                        {
                            throw new FormatException(string.Format("Escape character not allowed in un-escaped field: Index {0}", i));
                        }
                    }
                    #endregion
                    #region delimiter
                    else if (data[i] == d) // delimiter
                    {
                        // reset bools
                        beforeEsc = true;
                        inEsc = false;
                        afterEsc = false;
                        canEsc = true;

                        // add field to record
                        yield return data.Substring(datastarts, i - datastarts);

                        // add empty record if delimiter is at EOF
                        if (i + 1 == data.Length)
                            yield return string.Empty;

                        datastarts = i + 1;

                    }
                    #endregion
                    #region linebreak
                    else if (data[i] == cr && data.Length > i + 1 && data[i + 1] == lf) // linebreak
                    {
                        // reset bools
                        beforeEsc = true;
                        inEsc = false;
                        afterEsc = false;
                        canEsc = true;

                        // add field to record
                        yield return data.Substring(datastarts, i - datastarts);

                        // done reading record
                        yield break;
                    }
                    #endregion
                    #region EOF
                    else if (i == data.Length - 1) // EOF
                    {
                        // add field to record
                        yield return data.Substring(datastarts, i - datastarts + 1);
                    }
                    #endregion
                    #region other text
                    else // other text
                    {
                        canEsc = false;
                    }
                    #endregion
                }
                #endregion
                #region inEsc
                else if (inEsc)
                {
                    #region first of double escape
                    if (!isdblesc && data[i] == e && data.Length > i + 1 && data[i + 1] == e) // first of double escape
                    {
                        isdblesc = true;
                    }
                    #endregion
                    #region second of double escape
                    else if (data[i] == e && isdblesc) // second of double escape
                    {
                        isdblesc = false;
                    }
                    #endregion
                    #region escape character
                    else if (data[i] == e && !isdblesc) // escape character
                    {
                        // add field to record
                        yield return data.Substring(datastarts, i - datastarts).Replace(e.ToString() + e.ToString(), e.ToString());

                        // set bools
                        beforeEsc = false;
                        inEsc = false;
                        afterEsc = true;
                        canEsc = false;
                    }
                    #endregion
                    #region EOF
                    else if (i == data.Length - 1) // EOF
                    {
                        data += Environment.NewLine + base.ReadLine();
                    }
                    #endregion
                }
                #endregion
                #region afterEsc
                else if (afterEsc)
                {
                    #region whitespace
                    if (Char.IsWhiteSpace(data[i]) && !(data[i] == cr && data.Length > i + 1 && data[i + 1] == lf)) // whitespce (not linebreak)
                    {
                    }
                    #endregion
                    #region delimiter
                    else if (data[i] == d) // delimiter
                    {
                        //reset bools
                        beforeEsc = true;
                        inEsc = false;
                        afterEsc = false;
                        canEsc = true;

                        // add empty record if delimiter is at EOF
                        if (i + 1 == data.Length)
                            yield return string.Empty;

                        datastarts = i + 1;
                    }
                    #endregion
                    #region linebreak
                    else if (data[i] == cr && data.Length > i + 1 && data[i + 1] == lf) // linebreak
                    {
                        //reset bools
                        beforeEsc = true;
                        inEsc = false;
                        afterEsc = false;
                        canEsc = true;

                        // done reading record
                        yield break;
                    }
                    #endregion
                    #region other text
                    else // other text
                    {
                        throw new FormatException(string.Format("Delimiter, line break, or whitespace expected: Index {0}", i));
                    }
                    #endregion
                }
                #endregion
            }
        } 
        #endregion
    }
}
